<!--
 * @Description  : 
 * @Version      : 1.0
 * @Author       : seraph
 * @Date         : 2025-02-18 17:00:08
 * @LastEditors  : seraph
 * @LastEditTime : 2025-02-19 18:23:05
-->
<template>
    <h3>通过 key 管理状态 </h3>
    <p>Vue 默认按照“就地更新”的策略来更新通过 v-for 渲染的元素列表。当数据项的顺序改变时，Vue 不会随之移动 DOM 元素的顺序，而是就地更新每个元素，确保它们在原本指定的索引位置上渲染。</p>
    <p>默认模式是高效的，但只适用于列表渲染输出的结果不依赖子组件状态或者临时 DOM 状态 (例如表单输入值) 的情况。</p>
    <p>为了给 Vue 一个提示，以便它可以跟踪每个节点的标识，从而重用和重新排序现有的元素，你需要为每个元素对应的块提供一个唯一的 key attribute：</p>
    <pre><code class="html" v-html="highlighted(rawCode1, 'html')"></code></pre>
    <p><span>运行结果：</span></p>
    <ul>
        <li v-for="item in items" :key="item.id">{{ item.id }} - {{ item.name }}</li>
    </ul>
    <p>当你使用 &lt;template v-for&gt; 时，key 应该被放置在这个 &lt;template&gt; 容器上：</p>
    <pre><code class="html" v-html="highlighted(rawCode2, 'html')"></code></pre>
    <p><span>运行结果：</span></p>
    <ul>
        <template v-for="todo in todos" :key="todo.name">
            <li>{{ todo.name }}</li>
        </template>
    </ul>

    <h4>注意</h4>
    <p>key 在这里是一个通过 v-bind 绑定的特殊 attribute。请不要和在 v-for 中使用对象里所提到的对象属性名相混淆。</p>
    <p>推荐在任何可行的时候为 v-for 提供一个 key attribute，除非所迭代的 DOM 内容非常简单 (例如：不包含组件或有状态的 DOM 元素)，或者你想有意采用默认行为来提高性能。</p>
    <p>key 绑定的值期望是一个基础类型的值，例如字符串或 number 类型。不要用对象作为 v-for 的 key。关于 key attribute 的更多用途细节，请参阅 key API 文档。</p>
    <br>
</template>



<script lang="ts" setup>
import { ref, nextTick, watch, Ref, onMounted, reactive } from 'vue'
import hljs from 'highlight.js';
import 'highlight.js/styles/atom-one-dark.css';
import javascript from 'highlight.js/lib/languages/javascript';
import html from 'highlight.js/lib/languages/xml';

hljs.registerLanguage('javascript', javascript);
hljs.registerLanguage('html', html);

const highlighted = (code: string, type: string) => {
    return hljs.highlight(code, { language: type }).value.replace(/&amp;lt;/g, '&lt;').replace(/&amp;gt;/g, '&gt;')
}

const getRenderingHTML = (items: { element: Ref<HTMLElement | null>; html: Ref<string> }[]) => {
    items.forEach((item) => {
        nextTick(() => {
            if (item.element.value) {
                item.html.value = item.element.value.outerHTML
            }
        })
    })
}

const rawCode1 = `<ul>
  <li v-for="item in items" :key="item.id">{{ item.id }} - {{ item.name }}</li>
</ul>`

const items = ref([
    { id: 1, name: 'Item 1' },
    { id: 2, name: 'Item 2' },
    { id: 3, name: 'Item 3' }
])

const todos = ref([
    { name: '提出需求', isComplete: true },
    { name: '评审需求', isComplete: false },
    { name: '开发需求', isComplete: false },
    { name: '测试需求', isComplete: false }
])

const rawCode2 = `<template v-for="todo in todos" :key="todo.name">
  <li>{{ todo.name }}</li>
</template>`

const rawCode3 = `// ts 脚本
const todos = ref([
    { name: '提出需求', isComplete: true },
    { name: '评审需求', isComplete: false },
    { name: '开发需求', isComplete: false },
    { name: '测试需求', isComplete: false }
])`








</script>

<style scoped>
p {
    font-size: 16px;
    padding: 2px;
    margin: 5px 0;
}

/* 设置预格式化文本的样式 */
pre {
    /* 背景色 */
    background-color: #f1ffed;
    /* 边框 */
    border: 1px solid #ccc;
    /* 缩小内边距 */
    padding: 5px 5px 5px 10px;
    /* 允许滚动 */
    overflow: auto;
    /* 可选：设置上下外边距 */
    margin: 0px 0;
    /* 允许换行 */
    /* normal: 连续的空白字符会被合并为一个空格。文本会在允许的断点处自动换行。
     nowrap：连续的空白字符会被合并为一个空格。文本不会自动换行，即使在允许的断点处也不会换行。
     pre：保留所有的空白字符（包括空格、制表符和换行符）。文本只会在遇到换行符或者 <br> 标签时换行。
     pre-wrap：保留所有的空白字符。文本会在遇到换行符、<br> 标签，或者达到容器边界时换行。
     pre-line：合并连续的空白字符为一个空格，但保留换行符。文本会在遇到换行符、<br> 标签，或者达到容器边界时换行。
   */
    white-space: pre-wrap;
    /* 行高 */
    line-height: 120%;
    /* 定义缩进 */
    tab-size: 4;
}

h3,
h4,
h5,
h6 {
    margin: 25px 0 10px 0;
    line-height: 100%;
}

.bolder {
    font-weight: bolder;
}

.red {
    color: red;
}

button {
    margin: 5px 5px 5px 10px;
    line-height: 150%;
    width: auto;
    padding: 0 10px;
}

.active {
    color: red;
}

.static {
    font-weight: bolder;
}

.text-danger {
    font-style: italic;
}
</style>
